﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Policy;
using System.Runtime.Remoting.Lifetime;
using System.Reflection;
using System.Linq.Expressions;
using System.Threading.Tasks;
using System.IO;

namespace WorkStation.Frameworks.Core
{
    /// <summary>
    /// * 程序域控制器
    /// </summary>
    public class AppDomainLoader
    {
        protected AppDomain _appDomain;
        protected AppDomainProxy _appDomainProxy;

        /// <summary>
        /// * 程序域运行状态。
        /// </summary>
        public enum AppDomainState
        {
            /// <summary>
            /// * 运行
            /// </summary>
            Run,

            /// <summary>
            /// * 卸载
            /// </summary>
            Upload
        };

        /// <summary>
        /// * 当前程序域运行状态。
        /// </summary>
        public AppDomainState CurrentAppDomainState
        {
            get
            {
                if (this._appDomain == null)
                {
                    return AppDomainState.Upload;
                }
                else
                {
                    return AppDomainState.Run;
                }
            }
        }

        /// <summary>
        /// * 返回当前程序域名称
        /// </summary>
        public string CurrentAppDomainName
        {
            get { return this._appDomain.FriendlyName; }
        }

        /// <summary>
        /// * 当前控制器所操作的程序域
        /// </summary>
        public AppDomain CurrentAppDomain
        {
            get { return this._appDomain; }
        }

        /// <summary>
        /// * 当前程序域代理
        /// </summary>
        public AppDomainProxy CurrentAppDomainProxy
        {
            get { return this._appDomainProxy; }
        }

        public AppDomainLoader(string domainName, string assemblyPath, string privateBinPath)
        {
            AppDomainSetup setup = new AppDomainSetup();

            setup.ApplicationBase = assemblyPath;
            setup.ApplicationName = domainName;
            setup.ShadowCopyFiles = "true";
            setup.ShadowCopyDirectories = assemblyPath;
            setup.LoaderOptimization = LoaderOptimization.MultiDomain;
            setup.CachePath = Path.Combine(privateBinPath, "CachePath");
            setup.PrivateBinPath = privateBinPath;

            this._appDomain = AppDomain.CreateDomain(domainName, new Evidence(AppDomain.CurrentDomain.Evidence), setup);

            this._appDomainProxy = this._appDomain.CreateInstanceAndUnwrap("WorkStation.Frameworks.Core", "WorkStation.Frameworks.Core.AppDomainProxy") as WorkStation.Frameworks.Core.AppDomainProxy;

            #region * 远程对象设置租约（AppDomain建立的对象相当于远程对象）
            ILease lease = (ILease)this._appDomainProxy.GetLifetimeService();
            lease.Renew(new TimeSpan(9999, 9999, 9999, 9999, 9999));
            #endregion
        }

        public AppDomainLoader(string domainName, string shadowCopyDirectories, string assemblyName, string appDomainProxyType, params object[] args) 
        {
            AppDomainSetup setup = new AppDomainSetup();

            setup.ApplicationBase = shadowCopyDirectories;
            setup.ApplicationName = domainName;
            setup.ShadowCopyFiles = "true";
            setup.ShadowCopyDirectories = shadowCopyDirectories;
            setup.LoaderOptimization = LoaderOptimization.MultiDomain;
            setup.PrivateBinPath = shadowCopyDirectories;

            this._appDomain = AppDomain.CreateDomain(domainName, new Evidence(AppDomain.CurrentDomain.Evidence), setup);
          
            if (args == null)
            {
                this._appDomainProxy = this._appDomain.CreateInstanceAndUnwrap(assemblyName, appDomainProxyType) as AppDomainProxy;
            }
            else
            {
                this._appDomainProxy = this._appDomain.CreateInstanceAndUnwrap(assemblyName, appDomainProxyType, false, BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance, null, args, null, null) as AppDomainProxy;
            }

            #region * 远程对象设置租约（AppDomain建立的对象相当于远程对象）
            ILease lease = (ILease)this._appDomainProxy.GetLifetimeService();
            lease.Renew(new TimeSpan(9999, 9999, 9999, 9999, 9999));
            #endregion
        }

        /// <summary>
        /// * 设置程序域所用的AppConfig
        /// </summary>
        /// <param name="file"></param>
        public void SetAppDomainConfig(string file)
        {
            if (File.Exists(file))
            {
                this._appDomain.SetData("APP_CONFIG_FILE", file);
            }
        }

        /// <summary>
        /// * 加载一个程序集
        /// </summary>
        /// <param name="assemblyName"></param>
        /// <returns></returns>
        public string LoadAssembly(string assemblyName)
        {
            //Assembly[] list = this._appDomain.GetAssemblies();
            return this._appDomainProxy.LoadAssembly(assemblyName);
        }

        /// <summary>
        /// * 执行一个方法（经过验证，委托所代理执行的方法如果是static的，则将在子程序域执行，如果不是，则在主程序域执行。）2011-02-24
        /// </summary>
        /// <param name="methodHandle"></param>
        /// <param name="entity"></param>
        public void CallMethod(AppDomainProxy.CallMethodHandle methodHandle, MethodArgsEntity entity)
        {
            if (methodHandle != null)
            {
                this._appDomainProxy.CallMethod(methodHandle, entity);
            }
        }

        public TResult ExecuteMethod<TResult, TInterface>(Func<TInterface, TResult> func) where TInterface : class
        {
            return this._appDomainProxy.ExecuteMethod<TResult, TInterface>(func);
        }

        /// <summary>
        /// * 卸载当前程序域
        /// </summary>
        public void UnLoad()
        {
            AppDomain.Unload(this._appDomain);
            
            this._appDomain = null;
        }
    }

    // Func<string domainName, string shadowCopyDirectories, AppDomainLoader<TAppDomainProxy>>, (domainName, shadowCopyDirectories) => new AppDomainLoader<TAppDomainProxy>(domainName, shadowCopyDirectories);
    /// <summary>
    /// * 程序域控制器
    /// </summary>
    //public class AppDomainLoader<TAppDomainProxy> : AppDomainLoader where TAppDomainProxy : AppDomainProxy
    //{
    //    public AppDomainLoader(string domainName, string shadowCopyDirectories, params object[] args)
    //        : base(domainName, shadowCopyDirectories)
    //    {
    //        string proxyTypeName = typeof(TAppDomainProxy).FullName;
    //        Assembly classAssembly = AppDomain.CurrentDomain.GetAssemblies().SingleOrDefault(assembly => assembly.GetType(proxyTypeName) != null);

    //        if (args == null)
    //        {
    //            this._appDomainProxy = this._appDomain.CreateInstanceAndUnwrap(classAssembly.FullName, proxyTypeName) as TAppDomainProxy;
    //        }
    //        else
    //        {
    //            this._appDomainProxy = this._appDomain.CreateInstanceAndUnwrap(classAssembly.FullName, proxyTypeName, false, BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance, null, args, null, null) as TAppDomainProxy;
    //        }

    //        #region * 远程对象设置租约（AppDomain建立的对象相当于远程对象）
    //        ILease lease = (ILease)this._appDomainProxy.GetLifetimeService();
    //        lease.Renew(new TimeSpan(9999, 9999, 9999, 9999, 9999));
    //        #endregion
    //    }

    //    public new TAppDomainProxy CurrentAppDomainProxy
    //    {
    //        get { return this._appDomainProxy as TAppDomainProxy; }
    //    }
    //}

    //public class AppDomainLoaderFactory 
    //{
    //    protected static object _lockObject = new object();
    //    protected static Dictionary<string, Func<string, string, AppDomainLoader>> _genericDelegates = new Dictionary<string, Func<string, string, AppDomainLoader>>();
    //    protected static Dictionary<string, Func<string, string, string, string, AppDomainLoader>> _delegates = new Dictionary<string, Func<string, string, string, string, AppDomainLoader>>();

    //    public static AppDomainLoader CreateGenericAppDomainLoader(string domainName, string shadowCopyDirectories, string appDomainProxyType)
    //    {
    //        Func<string, string, AppDomainLoader> func = null;

    //        if (!_genericDelegates.TryGetValue(appDomainProxyType, out func))
    //        {
    //            lock (_lockObject)
    //            {
    //                if (!_genericDelegates.TryGetValue(appDomainProxyType, out func))
    //                {
    //                    func = GetGenericFunc(domainName, shadowCopyDirectories, appDomainProxyType);
    //                    _genericDelegates.Add(appDomainProxyType, func);
    //                }
    //            }
    //        }

    //        return func(domainName, shadowCopyDirectories);
    //    }

    //    public static AppDomainLoader CreateAppDomainLoader(string domainName, string shadowCopyDirectories, string assemblyName, string appDomainProxyType) 
    //    {
    //        Func<string, string, string, string, AppDomainLoader> func = null;

    //        if (!_delegates.TryGetValue(appDomainProxyType, out func))
    //        {
    //            lock (_lockObject)
    //            {
    //                if (!_delegates.TryGetValue(appDomainProxyType, out func))
    //                {
    //                    func = GetFunc();
    //                    _delegates.Add(typeof(AppDomainLoader).FullName, func);
    //                }
    //            }
    //        }

    //        return func(domainName, shadowCopyDirectories, assemblyName, appDomainProxyType);
    //    }

    //    protected static Func<string, string, string, string, AppDomainLoader> GetFunc() 
    //    {
    //        var sourceType = typeof(AppDomainLoader);

    //        var p1 = Expression.Parameter(typeof(string), "p1");
    //        var p2 = Expression.Parameter(typeof(string), "p2");
    //        var p3 = Expression.Parameter(typeof(string), "p3");
    //        var p4 = Expression.Parameter(typeof(string), "p4");

    //        var constructor = sourceType.GetConstructor(new Type[] { typeof(string), typeof(string), typeof(string), typeof(string), typeof(object[]) });

    //        var newExpr = Expression.New(constructor, p1, p2, p3, p4, Expression.Constant(null, typeof(object[])));

    //        var lamda = Expression.Lambda<Func<string, string , string, string ,AppDomainLoader>>(newExpr, p1, p2, p3, p4);

    //        return lamda.Compile();
    //    }

    //    protected static Func<string, string, AppDomainLoader> GetGenericFunc(string domainName, string shadowCopyDirectories, string appDomainProxyType)
    //    {
    //        Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(v => v.GetType(appDomainProxyType) != null);

    //        var sourceType = typeof(AppDomainLoader<>).MakeGenericType(assembly.GetType(appDomainProxyType));

    //        var p1 = Expression.Parameter(typeof(string), "p1");
    //        var p2 = Expression.Parameter(typeof(string), "p2");

    //        var constructor = sourceType.GetConstructor(new Type[] { typeof(string), typeof(string), typeof(object[]) });

    //        var newExpr = Expression.New(constructor, p1, p2, Expression.Constant(null, typeof(object[])));

    //        var lambda = Expression.Lambda<Func<string, string, AppDomainLoader>>(newExpr, p1, p2);

    //        return lambda.Compile();
    //    }
    //}
}
